"""
Uploader for tc.mofashi.ltd API v1 (Bearer Token).

- Base URL: https://tc.mofashi.ltd/api/v1
- Endpoint: /upload (multipart/form-data)
  - file: required (image file)
  - strategy_id: optional (storage strategy)
  - Authorization: "Bearer <token>" (optional; without it, uploads as guest)
  - Accept: application/json (required)

Features
- Upload from file path, raw bytes, or base64 (supports data URL prefix)
- Optional strategy_id
- Returns parsed JSON, extracted URL, and rate limit headers
- Proxy handling consistent with other helpers (USE_PROXY / LOCAL_PROXY ...)

CLI examples
  python agent/image_uploader.py --token "Bearer ..." --file path/to/img.png
  python agent/image_uploader.py --token "Bearer ..." --b64 "data:image/png;base64,..." --name demo.png
  python agent/image_uploader.py --file path/to/img.png  # guest upload
"""

from __future__ import annotations

import base64
import json
import mimetypes
import os
from typing import Any, Dict, Optional

import requests


def _should_use_local_proxy() -> bool:
    keys = (
        "USE_PROXY",
        "FORCE_LOCAL_PROXY",
        "LOCAL_PROXY",
        "HTTP_PROXY",
        "HTTPS_PROXY",
        "ALL_PROXY",
        "http_proxy",
        "https_proxy",
        "all_proxy",
    )
    return any(bool(os.environ.get(k)) for k in keys)


def _local_proxies() -> dict:
    addr = (os.environ.get("LOCAL_PROXY") or "127.0.0.1:7897").strip()
    return {"http": f"http://{addr}", "https": f"http://{addr}"}


class MofashiImageUploader:
    def __init__(
        self,
        token: Optional[str] = None,
        base_url: str = "https://tc.mofashi.ltd/api/v1",
        upload_path: str = "/upload",
        timeout: float = 45.0,
    ) -> None:
        self.base_url = base_url.rstrip("/")
        self.upload_path = upload_path
        self.timeout = timeout
        # Minimal headers; requests will set multipart boundary automatically
        self.headers = {
            "Accept": "application/json",
            "User-Agent": (
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                "AppleWebKit/537.36 (KHTML, like Gecko) "
                "Chrome/120.0.0.0 Safari/537.36"
            ),
        }
        if token:
            # Accept either raw token (without Bearer) or full header value
            if token.lower().startswith("bearer "):
                self.headers["Authorization"] = token
            else:
                self.headers["Authorization"] = f"Bearer {token}"
        self.session = requests.Session()
        self.session.trust_env = False
        if _should_use_local_proxy():
            self.session.proxies = _local_proxies()

    @staticmethod
    def _guess_mime(name: str) -> str:
        mt, _ = mimetypes.guess_type(name)
        return mt or "image/png"

    @staticmethod
    def _strip_data_url_prefix(s: str) -> str:
        # Remove data URL prefix if present: data:image/png;base64,....
        if isinstance(s, str) and s.startswith("data:"):
            i = s.find(",")
            if i != -1:
                return s[i + 1 :]
        return s

    def _post_multipart(
        self,
        filename: str,
        file_bytes: bytes,
        mime: Optional[str] = None,
        strategy_id: Optional[int] = None,
        extra_fields: Optional[Dict[str, str]] = None,
    ) -> Dict[str, Any]:
        """Send the multipart/form-data POST to tc.mofashi.ltd/api/v1/upload"""
        mime = mime or self._guess_mime(filename)
        url = f"{self.base_url}{self.upload_path}"
        files = {
            "file": (filename, file_bytes, mime),
        }
        data: Dict[str, Any] = {}
        if strategy_id is not None:
            data["strategy_id"] = str(int(strategy_id))
        if extra_fields:
            for k, v in extra_fields.items():
                if k not in data:
                    data[k] = v

        try:
            resp = self.session.post(
                url,
                headers=self.headers,
                files=files,
                data=data,
                timeout=self.timeout,
            )
        except Exception as e:
            return {"ok": False, "error": str(e), "status_code": 0}

        # Try parse json, but also return raw text on failure
        try:
            js = resp.json()
        except Exception:
            js = None
        # Extract URL per API (data.links.url)
        url_out: Optional[str] = None
        if isinstance(js, dict):
            try:
                d = js.get("data") or {}
                if isinstance(d, dict):
                    links = d.get("links") or {}
                    if isinstance(links, dict) and isinstance(links.get("url"), str):
                        url_out = links.get("url")  # type: ignore
                if not url_out and isinstance(js.get("url"), str):
                    url_out = js.get("url")  # type: ignore
            except Exception:
                pass

        rate_limit = {
            "limit": resp.headers.get("X-RateLimit-Limit"),
            "remaining": resp.headers.get("X-RateLimit-Remaining"),
        }

        return {
            "ok": bool(resp.ok and (not isinstance(js, dict) or js.get("status") in (True, 1, "true"))),
            "status_code": resp.status_code,
            "url": url_out,
            "json": js,
            "text": None if js is not None else resp.text,
            "rate_limit": rate_limit,
        }

    # Public APIs
    def upload_file(
        self,
        path: str,
        strategy_id: Optional[int] = None,
        extra_fields: Optional[Dict[str, str]] = None,
    ) -> Dict[str, Any]:
        if not os.path.isfile(path):
            return {"ok": False, "error": f"file not found: {path}", "status_code": 0}
        name = os.path.basename(path)
        mime = self._guess_mime(name)
        try:
            with open(path, "rb") as f:
                data = f.read()
        except Exception as e:
            return {"ok": False, "error": str(e), "status_code": 0}
        return self._post_multipart(name, data, mime=mime, strategy_id=strategy_id, extra_fields=extra_fields)

    def upload_bytes(
        self,
        name: str,
        data: bytes,
        mime: Optional[str] = None,
        strategy_id: Optional[int] = None,
        extra_fields: Optional[Dict[str, str]] = None,
    ) -> Dict[str, Any]:
        if not isinstance(data, (bytes, bytearray)):
            return {"ok": False, "error": "data must be bytes", "status_code": 0}
        return self._post_multipart(name, bytes(data), mime=mime, strategy_id=strategy_id, extra_fields=extra_fields)

    def upload_base64(
        self,
        name: str,
        b64: str,
        mime: Optional[str] = None,
        strategy_id: Optional[int] = None,
        extra_fields: Optional[Dict[str, str]] = None,
    ) -> Dict[str, Any]:
        b64 = self._strip_data_url_prefix(b64 or "")
        try:
            raw = base64.b64decode(b64, validate=False)
        except Exception as e:
            return {"ok": False, "error": f"invalid base64: {e}", "status_code": 0}
        mime = mime or self._guess_mime(name)
        return self._post_multipart(name, raw, mime=mime, strategy_id=strategy_id, extra_fields=extra_fields)


if __name__ == "__main__":
    import argparse

    p = argparse.ArgumentParser(description="Upload image to tc.mofashi.ltd (API v1)")
    g = p.add_mutually_exclusive_group(required=True)
    g.add_argument("--file", dest="file", help="Path to image file")
    g.add_argument("--b64", dest="b64", help="Base64 string (optionally data URL)")
    p.add_argument("--name", help="Filename to use for upload (when --b64)")
    p.add_argument("--token", help="Bearer token (with or without 'Bearer ' prefix)")
    p.add_argument("--strategy-id", dest="strategy_id", type=int, help="Storage strategy id")
    args = p.parse_args()

    uploader = MofashiImageUploader(token=args.token)
    if args.file:
        res = uploader.upload_file(args.file, strategy_id=args.strategy_id)
    else:
        if not args.name:
            raise SystemExit("--name required when using --b64")
        res = uploader.upload_base64(args.name, args.b64, strategy_id=args.strategy_id)
    print(json.dumps(res, ensure_ascii=False, indent=2))

